mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
Init
This commit is contained in:
commit
d6e1a19f16
15 changed files with 4022 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.swp
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
YunoHost Admin
|
||||
==============
|
||||
|
||||
JS client for YunoHost API
|
1
home.ms
Normal file
1
home.ms
Normal file
|
@ -0,0 +1 @@
|
|||
Hello :)
|
30
index.html
Normal file
30
index.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="cache-control" content="no-cache" />
|
||||
<title>YunoHost admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>YunoHost</h1>
|
||||
|
||||
<div id="flash">
|
||||
</div>
|
||||
<div id="content">
|
||||
<a href="#/">Greet</a>
|
||||
</div>
|
||||
<div id="main">
|
||||
<a href="#/login">Grset</a>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="js/jquery-1.10.1.min.js"></script>
|
||||
<script type="text/javascript" src="js/mustache.js"></script>
|
||||
<script type="text/javascript" src="js/sammy.js"></script>
|
||||
<script type="text/javascript" src="js/sammy.mustache.js"></script>
|
||||
<script type="text/javascript" src="js/sammy.json.js"></script>
|
||||
<script type="text/javascript" src="js/sammy.storage.js"></script>
|
||||
<script type="text/javascript" src="js/sammy.flash.js"></script>
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
98
js/app.js
Normal file
98
js/app.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
app = Sammy('#main', function (sam) {
|
||||
|
||||
sam.use('Mustache', 'ms');
|
||||
|
||||
var store = new Sammy.Store({name: 'storage', type: 'session'});
|
||||
|
||||
var flash = function (level, message) {
|
||||
flashs = store.get('flash');
|
||||
if (!flashs) { flashs = {'info': [], 'fail': [], 'success': [] } }
|
||||
flashs[level].push(message);
|
||||
store.set('flash', flashs);
|
||||
|
||||
html = '';
|
||||
for(lvl in flashs) {
|
||||
flashs[lvl].forEach( function(msg) {
|
||||
html += '<div class="'+ lvl +'">'+ msg +'</div>';
|
||||
});
|
||||
}
|
||||
$('#flash').html(html);
|
||||
}
|
||||
|
||||
var api = function (uri, callback, method, data) {
|
||||
method = typeof method !== 'undefined' ? method : 'GET';
|
||||
data = typeof data !== 'undefined' ? data : {};
|
||||
auth = "Basic "+ btoa(store.get('user') +':'+ atob(store.get('password')));
|
||||
jQuery.ajax({
|
||||
url: store.get('url') + uri,
|
||||
type: method,
|
||||
crossdomain: true,
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
beforeSend: function(req) {
|
||||
req.setRequestHeader('Authorization', auth);
|
||||
}
|
||||
})
|
||||
.done(function(data) {
|
||||
console.log(data);
|
||||
result = data;
|
||||
})
|
||||
.fail(function() {
|
||||
alert('fail');
|
||||
result = false;
|
||||
})
|
||||
.always(function() {
|
||||
callback(result);
|
||||
});
|
||||
}
|
||||
|
||||
sam.after(function () {
|
||||
store.set('flash', {'info': [], 'fail': [], 'success': [] });
|
||||
});
|
||||
|
||||
sam.before({except: {path: '#/login'}}, function (req) {
|
||||
store.set('path', req.path);
|
||||
if (!store.get('user')) {
|
||||
req.redirect('#/login');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
sam.get('#/', function (c) {
|
||||
c.render('home.ms').swap();
|
||||
});
|
||||
|
||||
sam.get('#/users/:user', function (c) {
|
||||
c.swap('');
|
||||
api('/users/'+ c.params['user'], function(data) {
|
||||
c.render('user_info.ms', data).swap();
|
||||
});
|
||||
});
|
||||
|
||||
sam.get('#/login', function (c) {
|
||||
c.render('login.ms').swap();
|
||||
});
|
||||
|
||||
sam.post('#/login', function (c) {
|
||||
store.set('url', c.params['url']);
|
||||
store.set('user', c.params['user']);
|
||||
store.set('password', btoa(c.params['password']));
|
||||
api('/users', function(data) {
|
||||
if (data.error) {
|
||||
flash('fail', 'Error: '+ data.error);
|
||||
} else {
|
||||
flash('success', 'Connected :)');
|
||||
}
|
||||
if (store.get('path')) {
|
||||
c.redirect(store.get('path'));
|
||||
} else {
|
||||
c.redirect('#/');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
app.run('#/');
|
||||
});
|
6
js/jquery-1.10.1.min.js
vendored
Normal file
6
js/jquery-1.10.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
536
js/mustache.js
Normal file
536
js/mustache.js
Normal file
|
@ -0,0 +1,536 @@
|
|||
/*!
|
||||
* mustache.js - Logic-less {{mustache}} templates with JavaScript
|
||||
* http://github.com/janl/mustache.js
|
||||
*/
|
||||
|
||||
/*global define: false*/
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof exports === "object" && exports) {
|
||||
factory(exports); // CommonJS
|
||||
} else {
|
||||
var mustache = {};
|
||||
factory(mustache);
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(mustache); // AMD
|
||||
} else {
|
||||
root.Mustache = mustache; // <script>
|
||||
}
|
||||
}
|
||||
}(this, function (mustache) {
|
||||
|
||||
var whiteRe = /\s*/;
|
||||
var spaceRe = /\s+/;
|
||||
var nonSpaceRe = /\S/;
|
||||
var eqRe = /\s*=/;
|
||||
var curlyRe = /\s*\}/;
|
||||
var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
||||
|
||||
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
||||
// See https://github.com/janl/mustache.js/issues/189
|
||||
var RegExp_test = RegExp.prototype.test;
|
||||
function testRegExp(re, string) {
|
||||
return RegExp_test.call(re, string);
|
||||
}
|
||||
|
||||
function isWhitespace(string) {
|
||||
return !testRegExp(nonSpaceRe, string);
|
||||
}
|
||||
|
||||
var Object_toString = Object.prototype.toString;
|
||||
var isArray = Array.isArray || function (obj) {
|
||||
return Object_toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
||||
}
|
||||
|
||||
var entityMap = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
"/": '/'
|
||||
};
|
||||
|
||||
function escapeHtml(string) {
|
||||
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
|
||||
function Scanner(string) {
|
||||
this.string = string;
|
||||
this.tail = string;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the tail is empty (end of string).
|
||||
*/
|
||||
Scanner.prototype.eos = function () {
|
||||
return this.tail === "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to match the given regular expression at the current position.
|
||||
* Returns the matched text if it can match, the empty string otherwise.
|
||||
*/
|
||||
Scanner.prototype.scan = function (re) {
|
||||
var match = this.tail.match(re);
|
||||
|
||||
if (match && match.index === 0) {
|
||||
this.tail = this.tail.substring(match[0].length);
|
||||
this.pos += match[0].length;
|
||||
return match[0];
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Skips all text until the given regular expression can be matched. Returns
|
||||
* the skipped string, which is the entire tail if no match can be made.
|
||||
*/
|
||||
Scanner.prototype.scanUntil = function (re) {
|
||||
var match, pos = this.tail.search(re);
|
||||
|
||||
switch (pos) {
|
||||
case -1:
|
||||
match = this.tail;
|
||||
this.pos += this.tail.length;
|
||||
this.tail = "";
|
||||
break;
|
||||
case 0:
|
||||
match = "";
|
||||
break;
|
||||
default:
|
||||
match = this.tail.substring(0, pos);
|
||||
this.tail = this.tail.substring(pos);
|
||||
this.pos += pos;
|
||||
}
|
||||
|
||||
return match;
|
||||
};
|
||||
|
||||
function Context(view, parent) {
|
||||
this.view = view || {};
|
||||
this.parent = parent;
|
||||
this._cache = {};
|
||||
}
|
||||
|
||||
Context.make = function (view) {
|
||||
return (view instanceof Context) ? view : new Context(view);
|
||||
};
|
||||
|
||||
Context.prototype.push = function (view) {
|
||||
return new Context(view, this);
|
||||
};
|
||||
|
||||
Context.prototype.lookup = function (name) {
|
||||
var value = this._cache[name];
|
||||
|
||||
if (!value) {
|
||||
if (name == '.') {
|
||||
value = this.view;
|
||||
} else {
|
||||
var context = this;
|
||||
|
||||
while (context) {
|
||||
if (name.indexOf('.') > 0) {
|
||||
value = context.view;
|
||||
var names = name.split('.'), i = 0;
|
||||
while (value && i < names.length) {
|
||||
value = value[names[i++]];
|
||||
}
|
||||
} else {
|
||||
value = context.view[name];
|
||||
}
|
||||
|
||||
if (value != null) break;
|
||||
|
||||
context = context.parent;
|
||||
}
|
||||
}
|
||||
|
||||
this._cache[name] = value;
|
||||
}
|
||||
|
||||
if (typeof value === 'function') value = value.call(this.view);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
function Writer() {
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
Writer.prototype.clearCache = function () {
|
||||
this._cache = {};
|
||||
this._partialCache = {};
|
||||
};
|
||||
|
||||
Writer.prototype.compile = function (template, tags) {
|
||||
var fn = this._cache[template];
|
||||
|
||||
if (!fn) {
|
||||
var tokens = mustache.parse(template, tags);
|
||||
fn = this._cache[template] = this.compileTokens(tokens, template);
|
||||
}
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
Writer.prototype.compilePartial = function (name, template, tags) {
|
||||
var fn = this.compile(template, tags);
|
||||
this._partialCache[name] = fn;
|
||||
return fn;
|
||||
};
|
||||
|
||||
Writer.prototype.getPartial = function (name) {
|
||||
if (!(name in this._partialCache) && this._loadPartial) {
|
||||
this.compilePartial(name, this._loadPartial(name));
|
||||
}
|
||||
|
||||
return this._partialCache[name];
|
||||
};
|
||||
|
||||
Writer.prototype.compileTokens = function (tokens, template) {
|
||||
var self = this;
|
||||
return function (view, partials) {
|
||||
if (partials) {
|
||||
if (typeof partials === 'function') {
|
||||
self._loadPartial = partials;
|
||||
} else {
|
||||
for (var name in partials) {
|
||||
self.compilePartial(name, partials[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renderTokens(tokens, self, Context.make(view), template);
|
||||
};
|
||||
};
|
||||
|
||||
Writer.prototype.render = function (template, view, partials) {
|
||||
return this.compile(template)(view, partials);
|
||||
};
|
||||
|
||||
/**
|
||||
* Low-level function that renders the given `tokens` using the given `writer`
|
||||
* and `context`. The `template` string is only needed for templates that use
|
||||
* higher-order sections to extract the portion of the original template that
|
||||
* was contained in that section.
|
||||
*/
|
||||
function renderTokens(tokens, writer, context, template) {
|
||||
var buffer = '';
|
||||
|
||||
var token, tokenValue, value;
|
||||
for (var i = 0, len = tokens.length; i < len; ++i) {
|
||||
token = tokens[i];
|
||||
tokenValue = token[1];
|
||||
|
||||
switch (token[0]) {
|
||||
case '#':
|
||||
value = context.lookup(tokenValue);
|
||||
|
||||
if (typeof value === 'object') {
|
||||
if (isArray(value)) {
|
||||
for (var j = 0, jlen = value.length; j < jlen; ++j) {
|
||||
buffer += renderTokens(token[4], writer, context.push(value[j]), template);
|
||||
}
|
||||
} else if (value) {
|
||||
buffer += renderTokens(token[4], writer, context.push(value), template);
|
||||
}
|
||||
} else if (typeof value === 'function') {
|
||||
var text = template == null ? null : template.slice(token[3], token[5]);
|
||||
value = value.call(context.view, text, function (template) {
|
||||
return writer.render(template, context);
|
||||
});
|
||||
if (value != null) buffer += value;
|
||||
} else if (value) {
|
||||
buffer += renderTokens(token[4], writer, context, template);
|
||||
}
|
||||
|
||||
break;
|
||||
case '^':
|
||||
value = context.lookup(tokenValue);
|
||||
|
||||
// Use JavaScript's definition of falsy. Include empty arrays.
|
||||
// See https://github.com/janl/mustache.js/issues/186
|
||||
if (!value || (isArray(value) && value.length === 0)) {
|
||||
buffer += renderTokens(token[4], writer, context, template);
|
||||
}
|
||||
|
||||
break;
|
||||
case '>':
|
||||
value = writer.getPartial(tokenValue);
|
||||
if (typeof value === 'function') buffer += value(context);
|
||||
break;
|
||||
case '&':
|
||||
value = context.lookup(tokenValue);
|
||||
if (value != null) buffer += value;
|
||||
break;
|
||||
case 'name':
|
||||
value = context.lookup(tokenValue);
|
||||
if (value != null) buffer += mustache.escape(value);
|
||||
break;
|
||||
case 'text':
|
||||
buffer += tokenValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms the given array of `tokens` into a nested tree structure where
|
||||
* tokens that represent a section have two additional items: 1) an array of
|
||||
* all tokens that appear in that section and 2) the index in the original
|
||||
* template that represents the end of that section.
|
||||
*/
|
||||
function nestTokens(tokens) {
|
||||
var tree = [];
|
||||
var collector = tree;
|
||||
var sections = [];
|
||||
|
||||
var token;
|
||||
for (var i = 0, len = tokens.length; i < len; ++i) {
|
||||
token = tokens[i];
|
||||
switch (token[0]) {
|
||||
case '#':
|
||||
case '^':
|
||||
sections.push(token);
|
||||
collector.push(token);
|
||||
collector = token[4] = [];
|
||||
break;
|
||||
case '/':
|
||||
var section = sections.pop();
|
||||
section[5] = token[2];
|
||||
collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
|
||||
break;
|
||||
default:
|
||||
collector.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the values of consecutive text tokens in the given `tokens` array
|
||||
* to a single token.
|
||||
*/
|
||||
function squashTokens(tokens) {
|
||||
var squashedTokens = [];
|
||||
|
||||
var token, lastToken;
|
||||
for (var i = 0, len = tokens.length; i < len; ++i) {
|
||||
token = tokens[i];
|
||||
if (token) {
|
||||
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
||||
lastToken[1] += token[1];
|
||||
lastToken[3] = token[3];
|
||||
} else {
|
||||
lastToken = token;
|
||||
squashedTokens.push(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return squashedTokens;
|
||||
}
|
||||
|
||||
function escapeTags(tags) {
|
||||
return [
|
||||
new RegExp(escapeRegExp(tags[0]) + "\\s*"),
|
||||
new RegExp("\\s*" + escapeRegExp(tags[1]))
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks up the given `template` string into a tree of token objects. If
|
||||
* `tags` is given here it must be an array with two string values: the
|
||||
* opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
|
||||
* course, the default is to use mustaches (i.e. Mustache.tags).
|
||||
*/
|
||||
function parseTemplate(template, tags) {
|
||||
template = template || '';
|
||||
tags = tags || mustache.tags;
|
||||
|
||||
if (typeof tags === 'string') tags = tags.split(spaceRe);
|
||||
if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
|
||||
|
||||
var tagRes = escapeTags(tags);
|
||||
var scanner = new Scanner(template);
|
||||
|
||||
var sections = []; // Stack to hold section tokens
|
||||
var tokens = []; // Buffer to hold the tokens
|
||||
var spaces = []; // Indices of whitespace tokens on the current line
|
||||
var hasTag = false; // Is there a {{tag}} on the current line?
|
||||
var nonSpace = false; // Is there a non-space char on the current line?
|
||||
|
||||
// Strips all whitespace tokens array for the current line
|
||||
// if there was a {{#tag}} on it and otherwise only space.
|
||||
function stripSpace() {
|
||||
if (hasTag && !nonSpace) {
|
||||
while (spaces.length) {
|
||||
delete tokens[spaces.pop()];
|
||||
}
|
||||
} else {
|
||||
spaces = [];
|
||||
}
|
||||
|
||||
hasTag = false;
|
||||
nonSpace = false;
|
||||
}
|
||||
|
||||
var start, type, value, chr, token;
|
||||
while (!scanner.eos()) {
|
||||
start = scanner.pos;
|
||||
|
||||
// Match any text between tags.
|
||||
value = scanner.scanUntil(tagRes[0]);
|
||||
if (value) {
|
||||
for (var i = 0, len = value.length; i < len; ++i) {
|
||||
chr = value.charAt(i);
|
||||
|
||||
if (isWhitespace(chr)) {
|
||||
spaces.push(tokens.length);
|
||||
} else {
|
||||
nonSpace = true;
|
||||
}
|
||||
|
||||
tokens.push(['text', chr, start, start + 1]);
|
||||
start += 1;
|
||||
|
||||
// Check for whitespace on the current line.
|
||||
if (chr == '\n') stripSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// Match the opening tag.
|
||||
if (!scanner.scan(tagRes[0])) break;
|
||||
hasTag = true;
|
||||
|
||||
// Get the tag type.
|
||||
type = scanner.scan(tagRe) || 'name';
|
||||
scanner.scan(whiteRe);
|
||||
|
||||
// Get the tag value.
|
||||
if (type === '=') {
|
||||
value = scanner.scanUntil(eqRe);
|
||||
scanner.scan(eqRe);
|
||||
scanner.scanUntil(tagRes[1]);
|
||||
} else if (type === '{') {
|
||||
value = scanner.scanUntil(new RegExp('\\s*' + escapeRegExp('}' + tags[1])));
|
||||
scanner.scan(curlyRe);
|
||||
scanner.scanUntil(tagRes[1]);
|
||||
type = '&';
|
||||
} else {
|
||||
value = scanner.scanUntil(tagRes[1]);
|
||||
}
|
||||
|
||||
// Match the closing tag.
|
||||
if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
|
||||
|
||||
token = [type, value, start, scanner.pos];
|
||||
tokens.push(token);
|
||||
|
||||
if (type === '#' || type === '^') {
|
||||
sections.push(token);
|
||||
} else if (type === '/') {
|
||||
// Check section nesting.
|
||||
if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
|
||||
var openSection = sections.pop();
|
||||
if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
|
||||
} else if (type === 'name' || type === '{' || type === '&') {
|
||||
nonSpace = true;
|
||||
} else if (type === '=') {
|
||||
// Set the tags for the next time around.
|
||||
tags = value.split(spaceRe);
|
||||
if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
|
||||
tagRes = escapeTags(tags);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there are no open sections when we're done.
|
||||
var openSection = sections.pop();
|
||||
if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
|
||||
|
||||
tokens = squashTokens(tokens);
|
||||
|
||||
return nestTokens(tokens);
|
||||
}
|
||||
|
||||
mustache.name = "mustache.js";
|
||||
mustache.version = "0.7.2";
|
||||
mustache.tags = ["{{", "}}"];
|
||||
|
||||
mustache.Scanner = Scanner;
|
||||
mustache.Context = Context;
|
||||
mustache.Writer = Writer;
|
||||
|
||||
mustache.parse = parseTemplate;
|
||||
|
||||
// Export the escaping function so that the user may override it.
|
||||
// See https://github.com/janl/mustache.js/issues/244
|
||||
mustache.escape = escapeHtml;
|
||||
|
||||
// All Mustache.* functions use this writer.
|
||||
var defaultWriter = new Writer();
|
||||
|
||||
/**
|
||||
* Clears all cached templates and partials in the default writer.
|
||||
*/
|
||||
mustache.clearCache = function () {
|
||||
return defaultWriter.clearCache();
|
||||
};
|
||||
|
||||
/**
|
||||
* Compiles the given `template` to a reusable function using the default
|
||||
* writer.
|
||||
*/
|
||||
mustache.compile = function (template, tags) {
|
||||
return defaultWriter.compile(template, tags);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compiles the partial with the given `name` and `template` to a reusable
|
||||
* function using the default writer.
|
||||
*/
|
||||
mustache.compilePartial = function (name, template, tags) {
|
||||
return defaultWriter.compilePartial(name, template, tags);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compiles the given array of tokens (the output of a parse) to a reusable
|
||||
* function using the default writer.
|
||||
*/
|
||||
mustache.compileTokens = function (tokens, template) {
|
||||
return defaultWriter.compileTokens(tokens, template);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the `template` with the given `view` and `partials` using the
|
||||
* default writer.
|
||||
*/
|
||||
mustache.render = function (template, view, partials) {
|
||||
return defaultWriter.render(template, view, partials);
|
||||
};
|
||||
|
||||
// This is here for backwards compatibility with 0.4.x.
|
||||
mustache.to_html = function (template, view, partials, send) {
|
||||
var result = mustache.render(template, view, partials);
|
||||
|
||||
if (typeof send === "function") {
|
||||
send(result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}));
|
8
js/sammy-latest.min.js
vendored
Normal file
8
js/sammy-latest.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
106
js/sammy.flash.js
Normal file
106
js/sammy.flash.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery', 'sammy'], factory);
|
||||
} else {
|
||||
(window.Sammy = window.Sammy || {}).Flash = factory(window.jQuery, window.Sammy);
|
||||
}
|
||||
}(function ($, Sammy) {
|
||||
|
||||
Sammy.FlashHash = function() {
|
||||
this.now = {};
|
||||
};
|
||||
|
||||
$.extend(Sammy.FlashHash.prototype , {
|
||||
// @return [String] this Flash, rendered as an <ul>.
|
||||
toHTML: function() {
|
||||
var result = this._renderUL();
|
||||
this.clear();
|
||||
return result;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._clearHash(this);
|
||||
this._clearHash(this.now);
|
||||
},
|
||||
|
||||
// Callback on redirect.
|
||||
// @api private
|
||||
_onRedirect: function() {
|
||||
this._clearHash(this.now);
|
||||
},
|
||||
|
||||
// clear out all flash keys
|
||||
// @api private
|
||||
_clearHash: function(hash) {
|
||||
var key;
|
||||
for (key in hash) {
|
||||
if (key !== 'now' && hash.hasOwnProperty(key)) {
|
||||
delete hash[key];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_renderUL: function() {
|
||||
return '<ul class="flash">' +
|
||||
this._renderLIs(this) +
|
||||
this._renderLIs(this.now) +
|
||||
'</ul>';
|
||||
},
|
||||
|
||||
_renderLIs: function(hash) {
|
||||
var result = '',
|
||||
key;
|
||||
for (key in hash) {
|
||||
if (hash[key] && key !== 'now' && hash.hasOwnProperty(key)) {
|
||||
result = result + '<li class="' + key + '">' + hash[key] + '</li>';
|
||||
}
|
||||
}
|
||||
Sammy.log('rendered flash: ' + result);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
// Sammy.Flash is a plugin for storing and sending status messages to the client. It's API and use
|
||||
// is similar to Ruby on Rails' `flash` explained here:
|
||||
// [http://apidock.com/rails/ActionController/Flash](http://apidock.com/rails/ActionController/Flash)
|
||||
Sammy.Flash = function(app) {
|
||||
app.flash = new Sammy.FlashHash();
|
||||
|
||||
// *Helper* flash(key, value) get or set a flash message that will
|
||||
// be erased on the next render (but not on redirect).
|
||||
//
|
||||
// @param [String] key, the Flash key
|
||||
// @param [String] value, the new value; optional
|
||||
// @return [Sammy.FlashHash, String, null] if a key was given, the value for that key; else, the Flash
|
||||
app.helper('flash', function(key, value) {
|
||||
if (arguments.length === 0) {
|
||||
return this.app.flash;
|
||||
} else if (arguments.length === 2) {
|
||||
this.app.flash[key] = value;
|
||||
}
|
||||
return this.app.flash[key];
|
||||
});
|
||||
|
||||
// *Helper* flashNow(key, value) get or set a flash message that
|
||||
// will be erased on the next render or redirect.
|
||||
//
|
||||
// @param [String] key, the Flash key
|
||||
// @param [String] value, the new value; optional
|
||||
// @return [String, null] the value for the given key
|
||||
app.helper('flashNow', function(key, value) {
|
||||
if (arguments.length === 0) {
|
||||
return this.app.flash.now;
|
||||
}else if (arguments.length === 2) {
|
||||
this.app.flash.now[key] = value;
|
||||
}
|
||||
return this.app.flash.now[key];
|
||||
});
|
||||
|
||||
app.bind('redirect', function() {
|
||||
this.app.flash._onRedirect();
|
||||
});
|
||||
};
|
||||
|
||||
return Sammy.Flash;
|
||||
|
||||
}));
|
2135
js/sammy.js
Normal file
2135
js/sammy.js
Normal file
File diff suppressed because it is too large
Load diff
368
js/sammy.json.js
Normal file
368
js/sammy.json.js
Normal file
|
@ -0,0 +1,368 @@
|
|||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery', 'sammy'], factory);
|
||||
} else {
|
||||
(window.Sammy = window.Sammy || {}).JSON = factory(window.jQuery, window.Sammy);
|
||||
}
|
||||
}(function ($, Sammy) {
|
||||
|
||||
// json2.js - only included if native json does not exist
|
||||
// http://www.json.org/js.html
|
||||
if (!window.JSON) {
|
||||
window.JSON = {};
|
||||
}
|
||||
(function () {
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
|
||||
return this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z';
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string) {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ?
|
||||
'"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string' ? c :
|
||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' :
|
||||
'"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder) {
|
||||
|
||||
// Produce a string from holder[key].
|
||||
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
// What happens next depends on the value's type.
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
|
||||
// If the value is a boolean or null, convert it to a string. Note:
|
||||
// typeof null does not produce 'null'. The case is included here in
|
||||
// the remote chance that this gets fixed someday.
|
||||
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
|
||||
case 'object':
|
||||
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
// Is the value an array?
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
|
||||
v = partial.length === 0 ? '[]' :
|
||||
gap ? '[\n' + gap +
|
||||
partial.join(',\n' + gap) + '\n' +
|
||||
mind + ']' :
|
||||
'[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
k = rep[i];
|
||||
if (typeof k === 'string') {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0 ? '{}' :
|
||||
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
|
||||
mind + '}' : '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
// space parameter, and returns a JSON text. The replacer can be a function
|
||||
// that can replace values, or an array of strings that will select the keys.
|
||||
// A default replacer method can be provided. Use of the space parameter can
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
|
||||
var j;
|
||||
|
||||
function walk(holder, key) {
|
||||
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// But just to be safe, we want to reject all unexpected forms.
|
||||
|
||||
// We split the second stage into 4 regexp operations in order to work around
|
||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||
// we look to see that the remaining characters are only whitespace or ']' or
|
||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||
|
||||
if (/^[\],:{}\s]*$/.
|
||||
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
|
||||
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
|
||||
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
|
||||
// In the third stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
|
||||
return typeof reviver === 'function' ?
|
||||
walk({'': j}, '') : j;
|
||||
}
|
||||
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
|
||||
// Sammy.JSON is a simple wrapper around Douglas Crockford's ever-useful json2.js
|
||||
// (http://www.json.org/js.html]) Sammy.JSON includes the top level JSON object if
|
||||
// it doesn't already exist (a.k.a. does not override the native implementation that
|
||||
// some browsers include). It also adds a <tt>json()</tt> helper to a Sammy app when
|
||||
// included.
|
||||
Sammy.JSON = function(app) {
|
||||
|
||||
app.helpers({
|
||||
// json is a polymorphic function that translates objects aback and forth
|
||||
// from JSON to JS. If given a string, it will parse into JS, if given a JS
|
||||
// object it will stringify into JSON.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// var app = $.sammy(function() {
|
||||
// this.use(Sammy.JSON);
|
||||
//
|
||||
// this.get('#/', function() {
|
||||
// this.json({user_id: 123}); //=> "{\"user_id\":\"123\"}"
|
||||
// this.json("{\"user_id\":\"123\"}"); //=> [object Object]
|
||||
// this.json("{\"user_id\":\"123\"}").user_id; //=> "123"
|
||||
// });
|
||||
// })
|
||||
//
|
||||
//
|
||||
json: function(object) {
|
||||
if (typeof object == 'string') {
|
||||
return JSON.parse(object);
|
||||
} else {
|
||||
return JSON.stringify(object);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return Sammy.JSON;
|
||||
|
||||
}));
|
126
js/sammy.mustache.js
Normal file
126
js/sammy.mustache.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery', 'sammy', 'mustache'], factory);
|
||||
} else {
|
||||
(window.Sammy = window.Sammy || {}).Mustache = factory(window.jQuery, window.Sammy, window.Mustache);
|
||||
}
|
||||
}(function ($, Sammy, Mustache) {
|
||||
|
||||
// <tt>Sammy.Mustache</tt> provides a quick way of using mustache style templates in your app.
|
||||
// The plugin wraps the awesome mustache.js lib created and maintained by Jan Lehnardt
|
||||
// at http://github.com/janl/mustache.js
|
||||
//
|
||||
// Note: As of Sammy 0.7 the Mustache lib is not included in the templates source. Please download
|
||||
// mustache.js and include it before Sammy.Mustache.
|
||||
//
|
||||
// Mustache is a clever templating system that relys on double brackets {{}} for interpolation.
|
||||
// For full details on syntax check out the original Ruby implementation created by Chris Wanstrath at
|
||||
// http://github.com/defunkt/mustache
|
||||
//
|
||||
// By default using Sammy.Mustache in your app adds the <tt>mustache()</tt> method to the EventContext
|
||||
// prototype. However, just like <tt>Sammy.Template</tt> you can change the default name of the method
|
||||
// by passing a second argument (e.g. you could use the ms() as the method alias so that all the template
|
||||
// files could be in the form file.ms instead of file.mustache)
|
||||
//
|
||||
// ### Example #1
|
||||
//
|
||||
// The template (mytemplate.ms):
|
||||
//
|
||||
// <h1>{{title}}<h1>
|
||||
//
|
||||
// Hey, {{name}}! Welcome to Mustache!
|
||||
//
|
||||
// The app:
|
||||
//
|
||||
// var app = $.sammy(function() {
|
||||
// // include the plugin and alias mustache() to ms()
|
||||
// this.use('Mustache', 'ms');
|
||||
//
|
||||
// this.get('#/hello/:name', function() {
|
||||
// // set local vars
|
||||
// this.title = 'Hello!'
|
||||
// this.name = this.params.name;
|
||||
// // render the template and pass it through mustache
|
||||
// this.partial('mytemplate.ms');
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// $(function() {
|
||||
// app.run()
|
||||
// });
|
||||
//
|
||||
// If I go to #/hello/AQ in the browser, Sammy will render this to the <tt>body</tt>:
|
||||
//
|
||||
// <h1>Hello!</h1>
|
||||
//
|
||||
// Hey, AQ! Welcome to Mustache!
|
||||
//
|
||||
//
|
||||
// ### Example #2 - Mustache partials
|
||||
//
|
||||
// The template (mytemplate.ms)
|
||||
//
|
||||
// Hey, {{name}}! {{>hello_friend}}
|
||||
//
|
||||
//
|
||||
// The partial (mypartial.ms)
|
||||
//
|
||||
// Say hello to your friend {{friend}}!
|
||||
//
|
||||
// The app:
|
||||
//
|
||||
// var app = $.sammy(function() {
|
||||
// // include the plugin and alias mustache() to ms()
|
||||
// this.use('Mustache', 'ms');
|
||||
//
|
||||
// this.get('#/hello/:name/to/:friend', function(context) {
|
||||
// // fetch mustache-partial first
|
||||
// this.load('mypartial.ms')
|
||||
// .then(function(partial) {
|
||||
// // set local vars
|
||||
// context.partials = {hello_friend: partial};
|
||||
// context.name = context.params.name;
|
||||
// context.friend = context.params.friend;
|
||||
//
|
||||
// // render the template and pass it through mustache
|
||||
// context.partial('mytemplate.ms');
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// $(function() {
|
||||
// app.run()
|
||||
// });
|
||||
//
|
||||
// If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the <tt>body</tt>:
|
||||
//
|
||||
// Hey, AQ! Say hello to your friend dP!
|
||||
//
|
||||
// Note: You need to include the mustache.js file before this plugin.
|
||||
//
|
||||
Sammy.Mustache = function(app, method_alias) {
|
||||
|
||||
// *Helper* Uses Mustache.js to parse a template and interpolate and work with the passed data
|
||||
//
|
||||
// ### Arguments
|
||||
//
|
||||
// * `template` A String template. {{}} Tags are evaluated and interpolated by Mustache.js
|
||||
// * `data` An Object containing the replacement values for the template.
|
||||
// data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
|
||||
// * `partials` An Object containing one or more partials (String templates
|
||||
// that are called from the main template).
|
||||
//
|
||||
var mustache = function(template, data, partials) {
|
||||
data = $.extend({}, this, data);
|
||||
partials = $.extend({}, data.partials, partials);
|
||||
return Mustache.to_html(template, data, partials);
|
||||
};
|
||||
|
||||
// set the default method name/extension
|
||||
if (!method_alias) { method_alias = 'mustache'; }
|
||||
app.helper(method_alias, mustache);
|
||||
};
|
||||
|
||||
return Sammy.Mustache;
|
||||
|
||||
}));
|
583
js/sammy.storage.js
Normal file
583
js/sammy.storage.js
Normal file
|
@ -0,0 +1,583 @@
|
|||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery', 'sammy'], factory);
|
||||
} else {
|
||||
(window.Sammy = window.Sammy || {}).Storage = factory(window.jQuery, window.Sammy);
|
||||
}
|
||||
}(function ($, Sammy) {
|
||||
|
||||
// Sammy.Store is an abstract adapter class that wraps the multitude of in
|
||||
// browser data storage into a single common set of methods for storing and
|
||||
// retreiving data. The JSON library is used (through the inclusion of the
|
||||
// Sammy.JSON) plugin, to automatically convert objects back and forth from
|
||||
// stored strings.
|
||||
//
|
||||
// Sammy.Store can be used directly, but within a Sammy.Application it is much
|
||||
// easier to use the `Sammy.Storage` plugin and its helper methods.
|
||||
//
|
||||
// Sammy.Store also supports the KVO pattern, by firing DOM/jQuery Events when
|
||||
// a key is set.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// // create a new store named 'mystore', tied to the #main element, using HTML5 localStorage
|
||||
// // Note: localStorage only works on browsers that support it
|
||||
// var store = new Sammy.Store({name: 'mystore', element: '#element', type: 'local'});
|
||||
// store.set('foo', 'bar');
|
||||
// store.get('foo'); //=> 'bar'
|
||||
// store.set('json', {obj: 'this is an obj'});
|
||||
// store.get('json'); //=> {obj: 'this is an obj'}
|
||||
// store.keys(); //=> ['foo','json']
|
||||
// store.clear('foo');
|
||||
// store.keys(); //=> ['json']
|
||||
// store.clearAll();
|
||||
// store.keys(); //=> []
|
||||
//
|
||||
// ### Arguments
|
||||
//
|
||||
// The constructor takes a single argument which is a Object containing these possible options.
|
||||
//
|
||||
// * `name` The name/namespace of this store. Stores are unique by name/type. (default 'store')
|
||||
// * `element` A selector for the element that the store is bound to. (default 'body')
|
||||
// * `type` The type of storage/proxy to use (default 'memory')
|
||||
//
|
||||
// Extra options are passed to the storage constructor.
|
||||
// Sammy.Store supports the following methods of storage:
|
||||
//
|
||||
// * `memory` Basic object storage
|
||||
// * `data` jQuery.data DOM Storage
|
||||
// * `cookie` Access to document.cookie. Limited to 2K
|
||||
// * `local` HTML5 DOM localStorage, browswer support is currently limited.
|
||||
// * `session` HTML5 DOM sessionStorage, browswer support is currently limited.
|
||||
//
|
||||
Sammy.Store = function(options) {
|
||||
var store = this;
|
||||
this.options = options || {};
|
||||
this.name = this.options.name || 'store';
|
||||
this.element = this.options.element || 'body';
|
||||
this.$element = $(this.element);
|
||||
if ($.isArray(this.options.type)) {
|
||||
$.each(this.options.type, function(i, type) {
|
||||
if (Sammy.Store.isAvailable(type)) {
|
||||
store.type = type;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.type = this.options.type || 'memory';
|
||||
}
|
||||
this.meta_key = this.options.meta_key || '__keys__';
|
||||
this.storage = new Sammy.Store[Sammy.Store.stores[this.type]](this.name, this.element, this.options);
|
||||
};
|
||||
|
||||
Sammy.Store.stores = {
|
||||
'memory': 'Memory',
|
||||
'data': 'Data',
|
||||
'local': 'LocalStorage',
|
||||
'session': 'SessionStorage',
|
||||
'cookie': 'Cookie'
|
||||
};
|
||||
|
||||
$.extend(Sammy.Store.prototype, {
|
||||
// Checks for the availability of the current storage type in the current browser/config.
|
||||
isAvailable: function() {
|
||||
if ($.isFunction(this.storage.isAvailable)) {
|
||||
return this.storage.isAvailable();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
// Checks for the existance of `key` in the current store. Returns a boolean.
|
||||
exists: function(key) {
|
||||
return this.storage.exists(key);
|
||||
},
|
||||
// Sets the value of `key` with `value`. If `value` is an
|
||||
// object, it is turned to and stored as a string with `JSON.stringify`.
|
||||
// It also tries to conform to the KVO pattern triggering jQuery events on the
|
||||
// element that the store is bound to.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// var store = new Sammy.Store({name: 'kvo'});
|
||||
// $('body').bind('set-kvo-foo', function(e, data) {
|
||||
// Sammy.log(data.key + ' changed to ' + data.value);
|
||||
// });
|
||||
// store.set('foo', 'bar'); // logged: foo changed to bar
|
||||
//
|
||||
set: function(key, value) {
|
||||
var string_value = (typeof value == 'string') ? value : JSON.stringify(value);
|
||||
key = key.toString();
|
||||
this.storage.set(key, string_value);
|
||||
if (key != this.meta_key) {
|
||||
this._addKey(key);
|
||||
this.$element.trigger('set-' + this.name, {key: key, value: value});
|
||||
this.$element.trigger('set-' + this.name + '-' + key, {key: key, value: value});
|
||||
}
|
||||
// always return the original value
|
||||
return value;
|
||||
},
|
||||
// Returns the set value at `key`, parsing with `JSON.parse` and
|
||||
// turning into an object if possible
|
||||
get: function(key) {
|
||||
var value = this.storage.get(key);
|
||||
if (typeof value == 'undefined' || value == null || value == '') {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch(e) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
// Removes the value at `key` from the current store
|
||||
clear: function(key) {
|
||||
this._removeKey(key);
|
||||
return this.storage.clear(key);
|
||||
},
|
||||
// Clears all the values for the current store.
|
||||
clearAll: function() {
|
||||
var self = this;
|
||||
this.each(function(key, value) {
|
||||
self.clear(key);
|
||||
});
|
||||
},
|
||||
// Returns the all the keys set for the current store as an array.
|
||||
// Internally Sammy.Store keeps this array in a 'meta_key' for easy access.
|
||||
keys: function() {
|
||||
return this.get(this.meta_key) || [];
|
||||
},
|
||||
// Iterates over each key value pair passing them to the `callback` function
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// store.each(function(key, value) {
|
||||
// Sammy.log('key', key, 'value', value);
|
||||
// });
|
||||
//
|
||||
each: function(callback) {
|
||||
var i = 0,
|
||||
keys = this.keys(),
|
||||
returned;
|
||||
|
||||
for (i; i < keys.length; i++) {
|
||||
returned = callback(keys[i], this.get(keys[i]));
|
||||
if (returned === false) { return false; }
|
||||
}
|
||||
},
|
||||
// Filters the store by a filter function that takes a key value.
|
||||
// Returns an array of arrays where the first element of each array
|
||||
// is the key and the second is the value of that key.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// var store = new Sammy.Store;
|
||||
// store.set('one', 'two');
|
||||
// store.set('two', 'three');
|
||||
// store.set('1', 'two');
|
||||
// var returned = store.filter(function(key, value) {
|
||||
// // only return
|
||||
// return value === 'two';
|
||||
// });
|
||||
// // returned => [['one', 'two'], ['1', 'two']];
|
||||
//
|
||||
filter: function(callback) {
|
||||
var found = [];
|
||||
this.each(function(key, value) {
|
||||
if (callback(key, value)) {
|
||||
found.push([key, value]);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return found;
|
||||
},
|
||||
// Works exactly like filter except only returns the first matching key
|
||||
// value pair instead of all of them
|
||||
first: function(callback) {
|
||||
var found = false;
|
||||
this.each(function(key, value) {
|
||||
if (callback(key, value)) {
|
||||
found = [key, value];
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
},
|
||||
// Returns the value at `key` if set, otherwise, runs the callback
|
||||
// and sets the value to the value returned in the callback.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// var store = new Sammy.Store;
|
||||
// store.exists('foo'); //=> false
|
||||
// store.fetch('foo', function() {
|
||||
// return 'bar!';
|
||||
// }); //=> 'bar!'
|
||||
// store.get('foo') //=> 'bar!'
|
||||
// store.fetch('foo', function() {
|
||||
// return 'baz!';
|
||||
// }); //=> 'bar!
|
||||
//
|
||||
fetch: function(key, callback) {
|
||||
if (!this.exists(key)) {
|
||||
return this.set(key, callback.apply(this));
|
||||
} else {
|
||||
return this.get(key);
|
||||
}
|
||||
},
|
||||
// loads the response of a request to `path` into `key`.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// In /mytemplate.tpl:
|
||||
//
|
||||
// My Template
|
||||
//
|
||||
// In app.js:
|
||||
//
|
||||
// var store = new Sammy.Store;
|
||||
// store.load('mytemplate', '/mytemplate.tpl', function() {
|
||||
// s.get('mytemplate') //=> My Template
|
||||
// });
|
||||
//
|
||||
load: function(key, path, callback) {
|
||||
var s = this;
|
||||
$.get(path, function(response) {
|
||||
s.set(key, response);
|
||||
if (callback) { callback.apply(this, [response]); }
|
||||
});
|
||||
},
|
||||
|
||||
_addKey: function(key) {
|
||||
var keys = this.keys();
|
||||
if ($.inArray(key, keys) == -1) { keys.push(key); }
|
||||
this.set(this.meta_key, keys);
|
||||
},
|
||||
_removeKey: function(key) {
|
||||
var keys = this.keys();
|
||||
var index = $.inArray(key, keys);
|
||||
if (index != -1) { keys.splice(index, 1); }
|
||||
this.set(this.meta_key, keys);
|
||||
}
|
||||
});
|
||||
|
||||
// Tests if the type of storage is available/works in the current browser/config.
|
||||
// Especially useful for testing the availability of the awesome, but not widely
|
||||
// supported HTML5 DOM storage
|
||||
Sammy.Store.isAvailable = function(type) {
|
||||
try {
|
||||
return Sammy.Store[Sammy.Store.stores[type]].prototype.isAvailable();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Memory ('memory') is the basic/default store. It stores data in a global
|
||||
// JS object. Data is lost on refresh.
|
||||
Sammy.Store.Memory = function(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.namespace = [this.element, this.name].join('.');
|
||||
Sammy.Store.Memory.store = Sammy.Store.Memory.store || {};
|
||||
Sammy.Store.Memory.store[this.namespace] = Sammy.Store.Memory.store[this.namespace] || {};
|
||||
this.store = Sammy.Store.Memory.store[this.namespace];
|
||||
};
|
||||
$.extend(Sammy.Store.Memory.prototype, {
|
||||
isAvailable: function() { return true; },
|
||||
exists: function(key) {
|
||||
return (typeof this.store[key] != "undefined");
|
||||
},
|
||||
set: function(key, value) {
|
||||
return this.store[key] = value;
|
||||
},
|
||||
get: function(key) {
|
||||
return this.store[key];
|
||||
},
|
||||
clear: function(key) {
|
||||
delete this.store[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Data ('data') stores objects using the jQuery.data() methods. This has the advantadge
|
||||
// of scoping the data to the specific element. Like the 'memory' store its data
|
||||
// will only last for the length of the current request (data is lost on refresh/etc).
|
||||
Sammy.Store.Data = function(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.$element = $(element);
|
||||
};
|
||||
$.extend(Sammy.Store.Data.prototype, {
|
||||
isAvailable: function() { return true; },
|
||||
exists: function(key) {
|
||||
return !!this.$element.data(this._key(key));
|
||||
},
|
||||
set: function(key, value) {
|
||||
return this.$element.data(this._key(key), value);
|
||||
},
|
||||
get: function(key) {
|
||||
return this.$element.data(this._key(key));
|
||||
},
|
||||
clear: function(key) {
|
||||
this.$element.removeData(this._key(key));
|
||||
},
|
||||
_key: function(key) {
|
||||
return ['store', this.name, key].join('.');
|
||||
}
|
||||
});
|
||||
|
||||
// LocalStorage ('local') makes use of HTML5 DOM Storage, and the window.localStorage
|
||||
// object. The great advantage of this method is that data will persist beyond
|
||||
// the current request. It can be considered a pretty awesome replacement for
|
||||
// cookies accessed via JS. The great disadvantage, though, is its only available
|
||||
// on the latest and greatest browsers.
|
||||
//
|
||||
// For more info on DOM Storage:
|
||||
// https://developer.mozilla.org/en/DOM/Storage
|
||||
// http://www.w3.org/TR/2009/WD-webstorage-20091222/
|
||||
//
|
||||
Sammy.Store.LocalStorage = function(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
};
|
||||
$.extend(Sammy.Store.LocalStorage.prototype, {
|
||||
isAvailable: function() {
|
||||
return ('localStorage' in window) && (window.location.protocol != 'file:');
|
||||
},
|
||||
exists: function(key) {
|
||||
return (this.get(key) != null);
|
||||
},
|
||||
set: function(key, value) {
|
||||
return window.localStorage.setItem(this._key(key), value);
|
||||
},
|
||||
get: function(key) {
|
||||
return window.localStorage.getItem(this._key(key));
|
||||
},
|
||||
clear: function(key) {
|
||||
window.localStorage.removeItem(this._key(key));;
|
||||
},
|
||||
_key: function(key) {
|
||||
return ['store', this.element, this.name, key].join('.');
|
||||
}
|
||||
});
|
||||
|
||||
// .SessionStorage ('session') is similar to LocalStorage (part of the same API)
|
||||
// and shares similar browser support/availability. The difference is that
|
||||
// SessionStorage is only persistant through the current 'session' which is defined
|
||||
// as the length that the current window is open. This means that data will survive
|
||||
// refreshes but not close/open or multiple windows/tabs. For more info, check out
|
||||
// the `LocalStorage` documentation and links.
|
||||
Sammy.Store.SessionStorage = function(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
};
|
||||
$.extend(Sammy.Store.SessionStorage.prototype, {
|
||||
isAvailable: function() {
|
||||
return ('sessionStorage' in window) &&
|
||||
(window.location.protocol != 'file:') &&
|
||||
($.isFunction(window.sessionStorage.setItem));
|
||||
},
|
||||
exists: function(key) {
|
||||
return (this.get(key) != null);
|
||||
},
|
||||
set: function(key, value) {
|
||||
return window.sessionStorage.setItem(this._key(key), value);
|
||||
},
|
||||
get: function(key) {
|
||||
var value = window.sessionStorage.getItem(this._key(key));
|
||||
if (value && typeof value.value != "undefined") { value = value.value }
|
||||
return value;
|
||||
},
|
||||
clear: function(key) {
|
||||
window.sessionStorage.removeItem(this._key(key));;
|
||||
},
|
||||
_key: function(key) {
|
||||
return ['store', this.element, this.name, key].join('.');
|
||||
}
|
||||
});
|
||||
|
||||
// .Cookie ('cookie') storage uses browser cookies to store data. JavaScript
|
||||
// has access to a single document.cookie variable, which is limited to 2Kb in
|
||||
// size. Cookies are also considered 'unsecure' as the data can be read easily
|
||||
// by other sites/JS. Cookies do have the advantage, though, of being widely
|
||||
// supported and persistent through refresh and close/open. Where available,
|
||||
// HTML5 DOM Storage like LocalStorage and SessionStorage should be used.
|
||||
//
|
||||
// .Cookie can also take additional options:
|
||||
//
|
||||
// * `expires_in` Number of seconds to keep the cookie alive (default 2 weeks).
|
||||
// * `path` The path to activate the current cookie for (default '/').
|
||||
//
|
||||
// For more information about document.cookie, check out the pre-eminint article
|
||||
// by ppk: http://www.quirksmode.org/js/cookies.html
|
||||
//
|
||||
Sammy.Store.Cookie = function(name, element, options) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.options = options || {};
|
||||
this.path = this.options.path || '/';
|
||||
// set the expires in seconds or default 14 days
|
||||
this.expires_in = this.options.expires_in || (14 * 24 * 60 * 60);
|
||||
};
|
||||
$.extend(Sammy.Store.Cookie.prototype, {
|
||||
isAvailable: function() {
|
||||
return ('cookie' in document) && (window.location.protocol != 'file:');
|
||||
},
|
||||
exists: function(key) {
|
||||
return (this.get(key) != null);
|
||||
},
|
||||
set: function(key, value) {
|
||||
return this._setCookie(key, value);
|
||||
},
|
||||
get: function(key) {
|
||||
return this._getCookie(key);
|
||||
},
|
||||
clear: function(key) {
|
||||
this._setCookie(key, "", -1);
|
||||
},
|
||||
_key: function(key) {
|
||||
return ['store', this.element, this.name, key].join('.');
|
||||
},
|
||||
_getCookie: function(key) {
|
||||
var escaped = this._key(key).replace(/(\.|\*|\(|\)|\[|\])/g, '\\$1');
|
||||
var match = document.cookie.match("(^|;\\s)" + escaped + "=([^;]*)(;|$)");
|
||||
return (match ? match[2] : null);
|
||||
},
|
||||
_setCookie: function(key, value, expires) {
|
||||
if (!expires) { expires = (this.expires_in * 1000) }
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + expires);
|
||||
var set_cookie = [
|
||||
this._key(key), "=", value,
|
||||
"; expires=", date.toGMTString(),
|
||||
"; path=", this.path
|
||||
].join('');
|
||||
document.cookie = set_cookie;
|
||||
}
|
||||
});
|
||||
|
||||
// Sammy.Storage is a plugin that provides shortcuts for creating and using
|
||||
// Sammy.Store objects. Once included it provides the `store()` app level
|
||||
// and helper methods. Depends on Sammy.JSON (or json2.js).
|
||||
Sammy.Storage = function(app) {
|
||||
this.use(Sammy.JSON);
|
||||
|
||||
this.stores = this.stores || {};
|
||||
|
||||
// `store()` creates and looks up existing `Sammy.Store` objects
|
||||
// for the current application. The first time used for a given `'name'`
|
||||
// initializes a `Sammy.Store` and also creates a helper under the store's
|
||||
// name.
|
||||
//
|
||||
// ### Example
|
||||
//
|
||||
// var app = $.sammy(function() {
|
||||
// this.use(Sammy.Storage);
|
||||
//
|
||||
// // initializes the store on app creation.
|
||||
// this.store('mystore', {type: 'cookie'});
|
||||
//
|
||||
// this.get('#/', function() {
|
||||
// // returns the Sammy.Store object
|
||||
// this.store('mystore');
|
||||
// // sets 'foo' to 'bar' using the shortcut/helper
|
||||
// // equivilent to this.store('mystore').set('foo', 'bar');
|
||||
// this.mystore('foo', 'bar');
|
||||
// // returns 'bar'
|
||||
// // equivilent to this.store('mystore').get('foo');
|
||||
// this.mystore('foo');
|
||||
// // returns 'baz!'
|
||||
// // equivilent to:
|
||||
// // this.store('mystore').fetch('foo!', function() {
|
||||
// // return 'baz!';
|
||||
// // })
|
||||
// this.mystore('foo!', function() {
|
||||
// return 'baz!';
|
||||
// });
|
||||
//
|
||||
// this.clearMystore();
|
||||
// // equivilent to:
|
||||
// // this.store('mystore').clearAll()
|
||||
// });
|
||||
//
|
||||
// });
|
||||
//
|
||||
// ### Arguments
|
||||
//
|
||||
// * `name` The name of the store and helper. the name must be unique per application.
|
||||
// * `options` A JS object of options that can be passed to the Store constuctor on initialization.
|
||||
//
|
||||
this.store = function(name, options) {
|
||||
// if the store has not been initialized
|
||||
if (typeof this.stores[name] == 'undefined') {
|
||||
// create initialize the store
|
||||
var clear_method_name = "clear" + name.substr(0,1).toUpperCase() + name.substr(1);
|
||||
this.stores[name] = new Sammy.Store($.extend({
|
||||
name: name,
|
||||
element: this.element_selector
|
||||
}, options || {}));
|
||||
// app.name()
|
||||
this[name] = function(key, value) {
|
||||
if (typeof value == 'undefined') {
|
||||
return this.stores[name].get(key);
|
||||
} else if ($.isFunction(value)) {
|
||||
return this.stores[name].fetch(key, value);
|
||||
} else {
|
||||
return this.stores[name].set(key, value)
|
||||
}
|
||||
};
|
||||
// app.clearName();
|
||||
this[clear_method_name] = function() {
|
||||
return this.stores[name].clearAll();
|
||||
}
|
||||
// context.name()
|
||||
this.helper(name, function() {
|
||||
return this.app[name].apply(this.app, arguments);
|
||||
});
|
||||
// context.clearName();
|
||||
this.helper(clear_method_name, function() {
|
||||
return this.app[clear_method_name]();
|
||||
});
|
||||
}
|
||||
return this.stores[name];
|
||||
};
|
||||
|
||||
this.helpers({
|
||||
store: function() {
|
||||
return this.app.store.apply(this.app, arguments);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Sammy.Session is an additional plugin for creating a common 'session' store
|
||||
// for the given app. It is a very simple wrapper around `Sammy.Storage`
|
||||
// that provides a simple fallback mechanism for trying to provide the best
|
||||
// possible storage type for the session. This means, `LocalStorage`
|
||||
// if available, otherwise `Cookie`, otherwise `Memory`.
|
||||
// It provides the `session()` helper through `Sammy.Storage#store()`.
|
||||
//
|
||||
// See the `Sammy.Storage` plugin for full documentation.
|
||||
//
|
||||
Sammy.Session = function(app, options) {
|
||||
this.use(Sammy.Storage);
|
||||
// check for local storage, then cookie storage, then just use memory
|
||||
this.store('session', $.extend({type: ['local', 'cookie', 'memory']}, options));
|
||||
};
|
||||
|
||||
// Sammy.Cache provides helpers for caching data within the lifecycle of a
|
||||
// Sammy app. The plugin provides two main methods on `Sammy.Application`,
|
||||
// `cache` and `clearCache`. Each app has its own cache store so that
|
||||
// you dont have to worry about collisions. As of 0.5 the original Sammy.Cache module
|
||||
// has been deprecated in favor of this one based on Sammy.Storage. The exposed
|
||||
// API is almost identical, but Sammy.Storage provides additional backends including
|
||||
// HTML5 Storage. `Sammy.Cache` will try to use these backends when available
|
||||
// (in this order) `LocalStorage`, `SessionStorage`, and `Memory`
|
||||
Sammy.Cache = function(app, options) {
|
||||
this.use(Sammy.Storage);
|
||||
// set cache_partials to true
|
||||
this.cache_partials = true;
|
||||
// check for local storage, then session storage, then just use memory
|
||||
this.store('cache', $.extend({type: ['local', 'session', 'memory']}, options));
|
||||
};
|
||||
|
||||
return Sammy.Storage;
|
||||
|
||||
}));
|
15
login.ms
Normal file
15
login.ms
Normal file
|
@ -0,0 +1,15 @@
|
|||
<form action="/index.html#/login" id="form" method="post">
|
||||
<p>
|
||||
<label>Url:</label>
|
||||
<input type="text" name="url" />
|
||||
</p>
|
||||
<p>
|
||||
<label>Login:</label>
|
||||
<input type="text" name="user" />
|
||||
</p>
|
||||
<p>
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password" />
|
||||
</p>
|
||||
<input id="submit" type="submit" value="Gogogo" />
|
||||
</form>
|
5
user_info.ms
Normal file
5
user_info.ms
Normal file
|
@ -0,0 +1,5 @@
|
|||
<ul>
|
||||
<li>Fullname: {{Fullname}}</li>
|
||||
<li>Username: {{Username}}</li>
|
||||
<li>Mail: {{Mail}}</li>
|
||||
</ul>
|
Loading…
Add table
Reference in a new issue